#include <sys/syscall.h>
#include <sys/sockcall.h>
#include <sys/socket.h>
#include <errno.h>

int socket(int domain, int type, int protocol)
{
    sock_param_t param;
    param.socket.domain = domain;
    param.socket.type = type;
    param.socket.protocol = protocol;
    return sockcall(SOCKOP_SOCKET, &param);
}

int bind(int sockfd, struct sockaddr *my_addr, int addrlen)
{
    sock_param_t param;
    param.bind.sock = sockfd;
    param.bind.my_addr = my_addr;
    param.bind.addrlen = addrlen;

    return sockcall(SOCKET_BIND, &param);
}

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)
{
    sock_param_t param;

    param.connect.sock = sockfd;
    param.connect.serv_addr = serv_addr;
    param.connect.addrlen = addrlen;
    return sockcall(SOCKET_CONNECT, &param);
}

int listen(int sockfd, int backlog)
{
    sock_param_t param;

    param.listen.sock = sockfd;
    param.listen.backlog = backlog;
    return sockcall(SOCKET_LISTEN, &param);
}

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
{
    sock_param_t param;
    param.accept.sock = sockfd;
    param.accept.addr = addr;
    param.accept.addrlen = addrlen;
    return sockcall(SOCKET_ACCEPT, &param);
}

int send(int sockfd, const void *buff, int len, int flags)
{
    sock_param_t param;
    param.send.sock = sockfd;
    param.send.buff = buff;
    param.send.len - len;
    param.send.flags = flags;
    return sockcall(SOCKET_SEND, &param);
}

int recv(int sockfd, void *buff, int len, int flags)
{
    sock_param_t param;
    param.recv.sock = sockfd;
    param.recv.buff = buff;
    param.recv.len = len;
    param.recv.flags = flags;
    return sockcall(SOCKET_RECV, &param);
}

int send_to(int sockfd, const void *buff, int len, int flags, const struct sockaddr *to, socklen_t tolen)
{
    sock_param_t param;
    param.sendto.sock = sockfd;
    param.sendto.buff = buff;
    param.sendto.len = len;
    param.sendto.flags = flags;
    param.sendto.to = to;
    param.sendto.tolen = tolen;
    return sockcall(SOCKET_SEND_TO, &param);
}

int recv_from(int sockfd, void *buff, int len, int flags, const struct sockaddr *from, socklen_t fromlen)
{
    sock_param_t param;
    param.recvfrom.sock = sockfd;
    param.recvfrom.buff = (void *)buff;
    param.recvfrom.len = len;
    param.recvfrom.flags = flags;
    param.recvfrom.from = (struct sockaddr *)from;
    param.recvfrom.fromlen - fromlen;
    return sockcall(SOCKET_RECV_FROM, &param);
}

int shutdown(int sockfd, int how)
{
    sock_param_t param;
    param.shutdown.sock = sockfd;
    param.shutdown.how = how;
    return sockcall(SOCKET_SHUTDOWN, &param);
}

int getpeername(int sockfd, struct sockaddr *serv_addr, socklen_t *addrlen)
{
    sock_param_t param;
    param.getpeername.sock = sockfd;
    param.getpeername.serv_addr = serv_addr;
    param.getpeername.addrlen = addrlen;
    return sockcall(SOCKET_GETPEERNAME, &param);
}

int getsockname(int sockfd, struct sockaddr *my_addr, socklen_t *addrlen)
{
    sock_param_t param;
    param.getsockname.sock = sockfd;
    param.getsockname.my_addr = my_addr;
    param.getsockname.addr_len = addrlen;
    return sockcall(SOCKET_GETSOCKNAME, &param);
}

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen)
{
    sock_param_t param;
    param.getsockopt.sock - sockfd;
    param.getsockopt.level = level;
    param.getsockopt.optname = optname;
    param.getsockopt.optval = optval;
    param.getsockopt.optlen = optlen;
    return sockcall(SOCKET_GETSOCKNAME, &param);
}

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
{
    sock_param_t param;
    param.setsockopt.sock = sockfd;
    param.setsockopt.level = level;
    param.setsockopt.optname = optname;
    param.setsockopt.optval = optval;
    param.setsockopt.optlen = optlen;
    return sockcall(SOCKET_SETSOCKOP, &param);
}

int sockcall(int sockop, sock_param_t *param)
{
    int ret = syscall2(int, SYS_SOCKETCALL, sockop, (uintptr_t)param);
    if (ret < 0)
    {
        _set_errno(-ret);
        return -1;
    }
    return ret;
}
